const React = {
  //创建虚拟dom
  createElement(type, props, ...children) {
    return {
      type,
      props: {
        ...props,
        children: children.map((child) => {
          if (typeof child === "object") {
            return child;
          } else {
            return React.createTextElement(child);
          }
        })
      }
    };
  },
  //创建文本节点
  createTextElement(text) {
    return {
      type: "TEXT_ELEMENT",
      props: {
        nodeValue: text,
        children: []
      }
    };
  }
};

//时间切片处理逻辑

// 实现将耗时任务拆分成多个小的工作单元
let nextUnitOfWork = null; //下一个要处理的单元
let currentFiber = null; //当前的fiber
let preFiber = null; //旧的fiber
let deletions = null; //存储要删除的节点

function render(element, container) {
  console.log("element", element);
  console.log("container", container);

  //初始化fiber
  currentFiber = {
    dom: container,
    props: {
      children: [element]
    },
    alternate: preFiber //存储旧的fiber
  };
  nextUnitOfWork = currentFiber; //让下一个工作单元指向当前fiber
  deletions = []; //初始化deletions
}

function workLoop(deadline) {
  //deadline.timeRemaining()获取浏览器的空闲时间
  let shouldYield = false;
  //while循环，直到没有下一个工作单元或者浏览器没有空闲时间
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);

    shouldYield = deadline.timeRemaining() < 1;
  }
  if (!nextUnitOfWork && currentFiber) {
    //当没有下一个工作单元时（nextUnitOfWork 为 null），并且有一个待提交的“工作根”（currentFiber），就会调用 commitRoot() 将最终的结果应用到 DOM 中
    commitRoot();
  }
  requestIdleCallback(workLoop);
}

/**
 * requestIdleCallback: 在浏览器的空闲时间执行回调函数
 * param: 回调函数
 */
requestIdleCallback(workLoop);

//创建dom节点
function createDom(fiber) {
  const dom =
    fiber.type === "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(fiber.type);

  updateDom(dom, {}, fiber.props); //初始化挂载dom的属性
  return dom;
}

/**
 * 更新dom节点的属性
 */
function updateDom(dom, prevProps, nextProps) {
  //删除旧的属性 过滤出不等于children的属性并且新的属性值和旧的属性值不相等的属性
  Object.keys(prevProps)
    .filter((key) => key !== "children")
    .filter((name) => prevProps[name] !== nextProps[name])
    .forEach((key) => {
      dom[key] = "";
    });
  //添加新的属性 过滤出不等于children的属性并且新的属性值和旧的属性值不相等的属性
  Object.keys(nextProps)
    .filter((key) => key !== "children")
    .filter((name) => prevProps[name] !== nextProps[name])
    .forEach((key) => {
      dom[key] = nextProps[key];
    });
}

/**
 * description: 执行任务单元,并且返回下一个要处理的单元
 */
function performUnitOfWork(fiber) {
  //确保每个 Fiber 节点都在内存中有一个对应的 DOM 节点准备好，以便后续在提交阶段更新到实际的 DOM 树中
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  //实现子节点的fiber树
  const childrenElements = fiber.props.children;
  createFiberTreeByChildren(fiber, childrenElements);

  //返回下一个要处理的单元
  if (fiber.child) {
    //存在子节点返回子节点
    return fiber.child;
  }
  //不存在子节点返回兄弟节点或者父节点
  let siblingFiber = fiber;
  while (siblingFiber) {
    if (siblingFiber.sibling) {
      return siblingFiber.sibling;
    }
    siblingFiber = siblingFiber.parent;
  }
  //否则返回null，表示已经处理完所有的节点
  return null;
}

// 创建 Fiber 节点
function createFiber(element, parent) {
  return {
    type: element.type,
    props: element.props,
    parent,
    dom: null, // 关联的 DOM 节点
    child: null, // 子节点
    sibling: null, // 兄弟节点
    alternate: null, // 对应的前一次 Fiber 节点
    effectTag: null // 'PLACEMENT', 'UPDATE', 'DELETION'
  };
}

// Diff 算法: 将子节点与之前的 Fiber 树进行比较
function createFiberTreeByChildren(fiber, elements) {
  //形成fiber树
  let index = 0;
  let prevSibling = null;
  let oldFiber = fiber.alternate && fiber.alternate.child; // 旧的 Fiber 树

  while (index < elements.length || oldFiber !== null) {
    const element = elements[index];
    let newFiber = null;
    //1.复用
    const sameType = oldFiber && element && oldFiber.type === element.type; //类型相同可以复用

    if (sameType) {
      newFiber = {
        type: oldFiber.type,
        props: element.props, //属性可能更新，需要用新的
        parent: fiber,
        dom: oldFiber.dom, //dom元素复用之前的
        alternate: oldFiber,
        effectTag: "UPDATE" //打上更新标签
      };
    }

    //2.新增
    if (element && !sameType) {
      newFiber = createFiber(element, fiber);
      newFiber.effectTag = "PLACEMENT"; //打上新增标签
    }

    //3.删除
    if (oldFiber && !sameType) {
      oldFiber.effectTag = "DELETION"; //删除
      deletions.push(oldFiber);
    }
    //将旧的fiber指针指向旧的fiber的兄弟节点，继续循环
    if (oldFiber) oldFiber = oldFiber.sibling;

    if (index === 0) {
      //第一个子节点
      fiber.child = newFiber;
    } else if (element) {
      //不是第一个子节点
      prevSibling.sibling = newFiber;
    }
    //记录前一个兄弟节点
    prevSibling = newFiber;
    //继续循环，处理下一个子节点
    index++;
  }
}

// 提交更新到 DOM
function commitRoot() {
  deletions.forEach(commitWork); // 删除需要删除的 Fiber 节点
  commitWork(currentFiber.child);
  preFiber = currentFiber;
  currentFiber = null;
}

// 提交单个 Fiber 节点
function commitWork(fiber) {
  if (!fiber) {
    return;
  }

  const domParent = fiber.parent.dom;

  if (fiber.effectTag === "PLACEMENT" && fiber.dom != null) {
    domParent.appendChild(fiber.dom);
  } else if (fiber.effectTag === "UPDATE" && fiber.dom != null) {
    updateDom(fiber.dom, fiber.alternate.props, fiber.props);
  } else if (fiber.effectTag === "DELETION") {
    domParent.removeChild(fiber.dom);
  }

  commitWork(fiber.child);
  commitWork(fiber.sibling);
}

// test
render(
  React.createElement(
    "div",
    { root: 123 },
    React.createElement(
      "div",
      null,
      "hello vue",
      React.createElement("div", null, "hello angular1")
    )
  ),
  document.getElementById("root")
);

setTimeout(() => {
  render(
    React.createElement(
      "div",
      { root: 123 },
      React.createElement(
        "div",
        null,
        "hello react",
        React.createElement("div", null, "hello angular2")
      )
    ),
    document.getElementById("root")
  );
}, 2000);
